home *** CD-ROM | disk | FTP | other *** search
- ARTICLES: IPX/SPX for NetBIOS Developers
-
- Original article: (c) Copyright Novell, 1994
- Novell Professional Developer BULLETS
- January 1994 (Volume 6, Number 1)
- NwTP additions : (between angular brackets/ minus signs [- ... -])
-
- NetBIOS is a popular peer-to-peer communication method that it is
- supported under NetWare through a NetBIOS emulator. However, even though
- NetBIOS is supported, there are definite advantages to using Novell's
- "native tongue" protocols, IPX (Internet Packet eXchange) and SPX
- (Sequenced Packet eXchange), when doing peer-to-peer communication.
-
- This article discusses the advantages of using IPX/SPX and provides an
- introduction to Novell's IPX and SPX protocols for developers who have a
- working familiarity with NetBIOS.
-
- Why Use IPX/SPX?
-
- The most obvious reason to use IPX and SPX is to improve performance;
- since NetWare emulates NetBIOS, processing NetBIOS commands involves more
- overhead than processing IPX/SPX commands. NetWare encapsulates emulated
- NetBIOS packets within IPX packets before they go out on the wire, so
- moving to IPX/SPX allows you to "cut out the middleman."
-
- You lose no connectivity by switching protocols either, since the
- emulated NetBIOS layer cannot communicate with hardware NetBIOS systems.
- In fact, moving to IPX/SPX gives you a net gain in connectivity; NetWare
- has a 70% share of the network operating system market.
-
- Also, since the NetBIOS emulator adds an additional layer of complexity
- to packets being sent out, it is more difficult to troubleshoot problems.
- Emulating NetBIOS involves an extra driver and an extra set of potential
- incompatibilities. Generally speaking, since IPX and SPX are not
- dramatically different from NetBIOS, it makes your job easier to work
- with the protocols that NetWare is designed to support.
-
- Datagram Services
-
- Novell's IPX protocol provides almost the same functionality as NetBIOS
- datagrams. Both specifications deliver packets on a best-effort basis,
- but with no guarantee of delivery or sequencing. Both IPX and NetBIOS
- also provide the capability to send packets either to a single node or to
- multiple nodes. NetBIOS supports the multicast, or the sending of a
- datagram to a selected group of nodes with the same group name. Since IPX
- is address-based instead of name-based, this capability is not directly
- supported; instead IPX must send an individual packet to each node.
-
- NetBIOS also supports the broadcast datagram, a datagram that is
- broadcast to the entire internetwork. IPX supports broadcasts, but only
- to one subnet at a time. Usually, this restriction poses no problem,
- since mechanisms such as the NetWare Service Advertising Protocol (SAP)
- overcome this limitation.
-
- The data portion of a NetBIOS datagram is limited in length to 512 bytes,
- whereas IPX packets allow 546 bytes of data on all networks and can
- sometimes be substantially larger than that depending on the maximum
- packet size supported by network routers. Some networks can handle packet
- sizes of 4096 bytes or more.
-
- Session Services
-
- As in the relationship between IPX and NetBIOS datagrams, Novell's SPX
- protocol serves much the same function as the NetBIOS session. Both SPX
- and NetBIOS sessions provide guaranteed delivery and sequencing of
- packets, but at the cost of increased overhead.
-
- The primary difference between the two is the supported packet size.
- NetBIOS sessions support 64K packet sizes (128K with Chain Sends). SPX
- has the same 546-byte packet size limitation as IPX and, in fact, SPX
- allows slightly less data in a packet than IPX, since the SPX header
- requires an additional 12 bytes. SPX therefore supports 534 bytes of data
- on all networks with the potential for much larger packets if supported
- by the routers, although attaining a 64K packet size is unlikely.
-
- Probably the most noticeable difference between IPX/SPX and NetBIOS is
- how each addresses packets. IPX/SPX addresses packets using network,
- node, and socket numbers. NetBIOS uses unique names to address packets.
- Each workstation can be uniquely addressed using the network and node
- numbers.
-
- A workstation can then have as many open sockets as desired for receiving
- peer-to-peer data packets. Many methods exist for determining a
- workstation's network, node, and destination socket number, but for
- simplicity the example code in this article uses SAP to obtain this
- information.
-
- The Waiting Game
-
- With NetBIOS, you can choose to allow most NetBIOS commands to complete
- before returning control to the application, but most IPX/SPX commands
- return control immediately. In other words, most IPX/SPX commands are
- "no-wait" commands; there is no IPX/SPX "wait" counterpart.
-
- Since most NetBIOS developers use the "no-wait" variants, this difference
- should not pose a problem, but if you need to use a "wait," you can code
- it very simply by issuing the command and then looping on the in use
- field.
-
- Asynchronous Events
-
- IPX/SPX also has a feature that is not used with NetBIOS: the
- asynchronous event. An asynchronous event can be initiated at any time
- and, as the name implies, can be set to occur independent of an
- application's execution path. An event could be set up, for example, to
- automatically broadcast an IPX packet every 45 seconds. The application
- initiating this event could then continue processing and leave the timing
- and broadcasting of packets to the IPX event handler.
-
- The Network Control Block & the Event Control Block
-
- From a developer's perspective, the "core" of NetBIOS is the Network
- Control Block (NCB). IPX and SPX are based on an Event Control Block
- (ECB) and an IPX/SPX header. Figure 1 describes the fields in the ECB.
-
- *********************************************************
- Figure 1: The IPX/SPX Event Control Block [- C structure -]
-
- void far *linkAddress Set by IPX
- void (far *ESRAddress)() Equivalent to NetBIOS POST routine
- BYTE inUseFlag Set when the ECB is in use, zero
- when it is available
- BYTE completionCode Equivalent to NetBIOS Command
- Completion
- WORD socketNumber Socket number associated with ECB
- BYTE IPXWorkspace[4] Set by IPX
- BYTE driverWorkspace[12] Set by IPX
- BYTE immediateAddress[6] Node address of next "hop"
- WORD fragmentCount Number of buffer fragments in packet
- ECBFragment fragmentDescriptor[2] Address and size of fragment(s)
-
- END of FIGURE 1
- *********************************************************
-
- [- *********************************************************
- Figure 1a: The IPX/SPX Event Control Block (Pascal syntax)
-
- linkAddress :Pointer Set by IPX
- ESRAddress :Pointer Equivalent to NetBIOS
- POST routine
- InUseFlag :Byte; Set when the ECB is in use,
- zero when it is available
- CompletionCode :Byte; Equivalent to NetBIOS Command
- Completion
- SocketNumber :Word; Socket number associated
- with ECB
- IPXWorkspace :array[1..4] of byte; Set by IPX
- DriverWorkspace :array[1..12] of byte; Set by IPX
- ImmediateAddress:array[1..6] of byte; (Tnodeaddress)
- Node address of next "hop"
- FragmentCount :word; Number of buffer fragments
- in packet
- Fragment :array[1.. ] of Tfragment Address and size of
- fragment(s)
-
- (Note: this structure is declared as the Tecb type in the nwIPX unit)
-
- END of FIGURE 1a
- ********************************************************* -]
-
- Note that the ECB contains a field that has no equivalent in the NCB
- called the immediate address field. This field should be populated with
- the node address of the first "hop" on the way to the packet's ultimate
- destination. Novell provides an API call to populate this field, the
- IPXGetLocalTarget() API available in the NetWare Client SDK.
-
- IPX Send Example
-
- The sample code in this article includes simple examples written under
- DOS with the NetWare Client SDK. Figure 2 shows a routine sending an IPX
- packet.
-
- *********************************************************
- Figure 2: IPX Send [- C example -]
-
- /* Send "Hello!" to the station at network 0x11111111, node
- 0x222222222222, socket 0x3333 using IPX */
-
- void IPXSayHello()
- {
- char buffer[] = "Hello!";
- ECB ecb;
- IPXHeader header;
- int transTime;
- header.packetType = 4;
- memset(header.destination.network, 0x11, 4);
- memset(header.destination.node, 0x22, 6);
- memset(header.destination.socket, 0x33, 2);
-
- ecb.ESRAddress = NULL;
- ecb.socketNumber = 0x4444;
- IPXGetLocalTarget(header.destination,
- ecb.immediateAddress, &transTime);
- ecb.fragmentCount = 2;
- ecb.fragmentDescriptor[0].address = &header;
- ecb.fragmentDescriptor[0].size = sizeof(IPXHeader);
- ecb.fragmentDescriptor[1].address = buffer;
- ecb.fragmentDescriptor[1].size = strlen(buffer) + 1;
- IPXSendPacket(&ecb);
- }
-
- END of FIGURE 2
- *********************************************************
-
- [- *********************************************************
- Figure 2a: IPX Send (Pascal example)
-
- { Send "Hello!" to the station at network $11111111, node
- $222222222222, socket $3333 using IPX }
-
- Procedure IPXSayHello;
- Var buffer:string;
- ecb:Tecb;
- header:TipxHeader;
- transTime:Word;
- begin
- Buffer:="Hello!";
- header.packetType := 4;
- FillChar(header.destination.network,4,$11);
- FillChar(header.destination.node, 6,$22);
- FillChar(header.destination.socket, 2,$33);
-
- ecb.ESRAddress:=NIL;
- ecb.socketNumber:=$4444;
- IPXGetLocalTarget(header.destination,
- ecb.immediateAddress, transTime);
- ecb.fragmentCount:=2;
- ecb.fragment[1].address:= @header;
- ecb.fragment[1].size := SizeOf(TIPXHeader);
- ecb.fragment[2].address:= @buffer[1];
- ecb.fragment[2].size:= ord(buffer[0]);
- IPXSendPacket(ecb);
- end;
-
- END of FIGURE 2a
- ********************************************************* -]
-
-
- The first apparent difference between IPX and NetBIOS is that IPX uses
- two buffers where NetBIOS would use one. The first buffer is the IPX
- Header containing the source and destination addresses, the packet type,
- and several "housekeeping" fields. Refer to Figure 3 for a description of
- the IPX header.
-
- *********************************************************
- Figure 3: IPX Header
-
- WORD checkSum Included to conform to Xerox IDP standard
- Set to FFFF by IPX
- WORD length Length of entire IPX packet including
- header
- Set by IPX
- BYTE transportControl Hop count - Set to zero by IPX
- BYTE packetType IPX packet type is 4
- IPXAddress destination Address the packet is sent to
- [- Pascal: of type TInternetworkAddress -]
- IPXAddress source Address of node sending packet set by IPX
- [- Pascal: of type TinternetworkAddress -]
-
- END of FIGURE 3
- *********************************************************
-
- The second buffer is the data to be sent. Two fields in the IPX header
- must be set for an IPX send: the packet type and the destination address.
- IPX packets are type 4, SPX packets are type 5. [- Note that according
- to the original xerox definitions this statement is not correct. Type 4
- packets are reserved for the PEP protocol. Use type 0 (undefined) when
- transmitting standard IPX packets-] The destination address consists
- of a four-byte network number, a six-byte node number, and a two-byte
- socket number.
-
- If these examples used an Event Service Routine (ESR), the ESR address
- would be filled with the address of a procedure to be run when the send
- completes, but since NULL is specified, this routine will not be run. The
- ESR is equivalent to the NetBIOS POST routine. When the IPX send
- executes, the rest of the fields in the IPX header are filled in
- automatically, including the source address. You must specify the socket
- number to be included in the source address, but the socket need not be
- open to send a packet. For this example, socket number 0x4444 was
- arbitrarily chosen.
-
- The immediate address field described above must be filled in as well,
- and the IPXGetLocalTarget() API call fills in this field with the
- appropriate value. It is passed the final destination of the packet and
- it calculates the address of the "first hop" on the way to the final
- destination. Note that if the target workstation is on the same subnet as
- the sending workstation the immediate address will be the same as the
- final destination. Otherwise, it will be a bridge or router on the
- subnet.
-
- Each of the buffers sent in the IPX packet is considered to be a
- fragment. Since there are two buffers (the IPX header and the data), the
- fragment count is equal to two. The address and size of the fragments are
- then entered, starting with the IPX header. As soon as all of the
- relevant fields are filled, the example calls IPXSendPacket() and passes
- it the address of the ECB.
-
- Receiving an IPX packet is much like sending one from a programming
- standpoint, except that you do not need to set the IPX header fields. In
- the ECB, you should set the ESR address, socket number, immediate
- address, and fragment descriptors.
-
- Note about socket numbers: the socket number specified for an IPX send
- does not need to be open, but for an IPX receive the socket must be open.
- The API call to receive an IPX packet is IPXListenForPacket().
-
- SPX Connection Example
-
- Figure 4 contains a code sample that establishes an SPX connection.
- Before the request for an SPX connection is submitted, several ECBs are
- already listening for data (this is important). SPX temporarily "steals"
- two ECBs from the available and waiting ones for connection maintenance,
- and then it puts the stolen ECBs back in the pool when finished. If there
- are no pending ECBs for SPX to use, it cannot send an acknowledgement to
- the remote site and the connection will stall and time out.
-
- *********************************************************
- Figure 4: Establishing an SPX Connection [- C code example -]
-
- /* Start an SPX connection with the station at network
- 0x11111111, node 0x222222222222, socket 0x3333, use
- local socket 0x4444 */
-
- #define NUM_BUFFS 5
-
- void call()
- {
- ECB send, receive[NUM_BUFFS], connect, term;
- SPXHeader sendHdr, rcvHdr[NUM_BUFFS], connHdr;
- char buffer[NUM_BUFFS][80], sendbuf[] = "Hello!";
- int i, ccode, packetsReceived;
- WORD spxConnectionID;
-
- for (i = 0; i < NUM_BUFFS; i++) {
- receive[i].ESRAddress = NULL;
- receive[i].socketNumber = 0x4444;
- receive[i].fragmentCount = 2;
- receive[i].fragmentDescriptor[0].address
- = &(rcvHdr[i]);
- receive[i].fragmentDescriptor[0].size
- = sizeof(SPXHeader);
- receive[i].fragmentDescriptor[1].address
- = &(buffer[i]);
- receive[i].fragmentDescriptor[1].size = 80;
- SPXListenForSequencedPacket(receive[i]);
- }
-
- connect.ESRAddress = NULL;
- connect.socketNumber = 0x4444;
- connect.fragmentCount = 1;
- connect.fragmentDescriptor[0].address = &connHdr;
- connect.fragmentDescriptor[0].size
- = sizeof(SPXHeader);
-
- memset(connHdr.destination.network, 0x11, 4);
- memset(connHdr.destination.node, 0x22, 6);
- memset(connHdr.destination.socket, 0x33, 2);
-
- ccode = SPXEstablishConnection(0, 0,
- &spxConnectionID,
- &connect);
- printf("SPXEstablishConnection return code
- = 0x%x\n", ccode);
- if (ccode != 0)
- return;
- while (connect.inUseFlag != 0)
- IPXRelinquishControl();
- if (connect.completionCode != 0)
- return;
- send.ESRAddress = NULL;
- send.fragmentCount = 2;
- send.fragmentDescriptor[0].address = &sendHdr;
- send.fragmentDescriptor[0].size = sizeof(SPXHeader);
- send.fragmentDescriptor[1].address = sendbuf;
- send.fragmentDescriptor[1].size = 7;
- SPXSendSequencedPacket(spxConnectionID, &send);
-
- packetsReceived = 0;
- while (packetsReceived < 10) {
- for (i = 0; i < NUM_BUFFS; i++) {
- if (receive[i].inUseFlag != 0) {
- if (receive[i].completionCode != 0) {
- packetsReceived = 10;
- /* If we get an error, terminate */
- break;
- }
- printf("Received: %s\n", buffer[i]);
- packetsReceived++;
- }
- SPXListenForSequencedPacket(receive[i]);
- }
- IPXRelinquishControl();
- }
-
- term.ESRAddress = NULL;
- term.fragmentCount = 1;
- term.fragmentDescriptor[0].address = &connHdr;
- term.fragmentDescriptor[0].size = sizeof(SPXHeader);
- SPXTerminateConnection(spxConnectionID, &term);
- while (term.inUseFlag != 0)
- IPXRelinquishControl();
- for (i = 0; i < NUM_BUFFS; i++)
- IPXCancelEvent(receive[i]);
- }
- END of FIGURE 4
- *********************************************************
-
- [- *********************************************************
- Figure 4a: Establishing an SPX Connection (Pascal example)
-
- { Start an SPX connection with the station at network
- $11111111, node $222222222222, socket $3333, use
- local socket $4444 }
-
- CONST NUM_BUFFS=5;
-
- Procedure call;
- Var send,connect,term:Tecb;
- receive :array[1..NUM_BUFFS] of Tecb;
- sendHdr, connHdr : TspxHeader;
- rcvHdr :array[1..NUM_BUFFS] of TspxHeader;
- buffer :array[1..NUM_BUFFS] of string[80];
- sendBuf :string;
- i,packetsReceived:Integer;
- spxConnectionId :word;
- begin;
- sendbuf:="Hello!";
-
- for i:= 1 to NUM_BUFFS
- do begin
- receive[i].ESRAddress := NIL;
- receive[i].socketNumber := $4444;
- receive[i].fragmentCount = 2;
- receive[i].fragment[1].address := @rcvHdr[i];
- receive[i].fragment[1].size := sizeof(TSPXHeader);
- receive[i].fragment[2].address := @buffer[i];
- receive[i].fragment[2].size := 80;
- SPXListenForSequencedPacket(receive[i]);
- end;
-
- connect.ESRAddress := NIL;
- connect.socketNumber := $4444;
- connect.fragmentCount := 1;
- connect.fragment[1].address := @connHdr;
- connect.fragment[1].size := sizeof(TSPXHeader);
-
- FillChar(connHdr.destination.network, 4, $11);
- FillChar(connHdr.destination.node, 6, $22);
- FillChar(connHdr.destination.socket, 2, $33);
-
- IF NOT SPXEstablishConnection(0, 0,
- spxConnectionID,
- connect)
- then begin
- writeln('SPXEstablishConnection return code',
- HexStr(nwSpx.result,2));
- exit;
- end;
-
- while (connect.inUseFlag <> 0)
- do IPXRelinquishControl();
- if (connect.completionCode <> 0)
- then exit;
-
- send.ESRAddress := NIL;
- send.fragmentCount := 2;
- send.fragment[1].address = @sendHdr;
- send.fragment[1].size := sizeof(TSPXHeader);
- send.fragment[2].address := @sendbuf[0];
- send.fragment[2].size := ord(sendBuf[0])+1;
- SPXSendSequencedPacket(spxConnectionID, send);
-
- packetsReceived := 0;
- while (packetsReceived < 10)
- do begin
- for i :=1 to NUM_BUFFS
- do begin
- if (receive[i].inUseFlag <> 0)
- and (receive[i].completionCode <> 0)
- then begin
- packetsReceived := 10;
- exit;
- { If we get an error, terminate }
- end;
- writeln('Received: ", buffer[i]);
- inc(packetsReceived);
- SPXListenForSequencedPacket(receive[i]);
- end;
- IPXRelinquishControl;
- end;
-
- term.ESRAddress := NIL;
- term.fragmentCount := 1;
- term.fragment[1].address := @connHdr;
- term.fragment[1].size := sizeof(TSPXHeader);
- SPXTerminateConnection(spxConnectionID, term);
- while (term.inUseFlag <> 0)
- do IPXRelinquishControl;
- for i:=1 to NUM_BUFFS
- do IPXCancelEvent(receive[i]);
- end;
-
- END of FIGURE 4a
- ********************************************************* -]
-
- This process may sound complicated, but everything happens transparently.
- As long as there are extra ECBs available, the application never knows
- they have been borrowed, since SPX puts them back in the exact same state
- they were in when they were pressed into service.
-
- If the connection is established with the SPX watchdog enabled, the
- watchdog monitors the connection and notifies the application if the
- connection fails, even if the application is not currently sending data
- over the connection. This feature is useful for applications that start
- SPX connections, but use them infrequently. For simplicity, however, the
- example does not use the SPX watchdog.
-
- After the listen ECBs have been posted, the connection ECB is then set up
- in much the same way the IPX send ECB was, except that this ECB has only
- one fragment: the SPX header. The destination network, node, and socket
- also are set the same way they were in the previous example.
-
- SPXEstablishConnection() is passed a retry count of zero, indicating that
- you should use the default value for number of retries. This value is set
- in the workstation's NET.CFG file using the IPX RETRY COUNT parameter,
- which defaults to 20. The last zero passed in SPXEstablishConnection()
- indicates not to use the SPX watchdog. The SPX connection ID is returned
- as the third parameter. The SPX connection ID can be considered
- equivalent to the NetBIOS local session number.
-
- Next, the sample code attempts to establish a connection. It polls the
- ECB's in use flag waiting for the event to complete. The
- IPXRelinquishControl() call is very important at this stage. If the code
- did nothing but sit in a tight loop, IPX and SPX would never get the
- chance to do any processing. IPXRelinquishControl() allows the IPX/SPX
- layer to get some work done.
-
- Once the in use flag is set to zero, the example checks the return code
- to see if the attempt to establish a connection was successful. The code
- does not illustrate how to handle the various failure cases, but the most
- likely cause of a failure would be that the other side is not yet
- listening for a connection, just like in NetBIOS. After establishing the
- connection, packets can be sent to the remote station.
-
- The SPXSendSequencedPacket() call requires much less information than its
- IPX counterpart. Since the connection is already established, all
- SPXSendSequencedPacket() needs is the SPX connection ID, an ESR address,
- and the fragment information.
-
- After sending a packet, the example program waits for ten packets to
- arrive. When an ECB comes back, the example displays the data and then
- re-submits the ECB so that it can be used to receive a packet again.
- After receiving ten packets, it issues an SPXTerminateConnection() call
- to notify the other side that it is done.
-
- The call to terminate the connection takes almost the same parameters
- that the establish connection call does, except that there is no need to
- fill out any information in the SPX header. Once the connection has been
- terminated, the pending listen ECBs must be cancelled. To do so, the
- example calls IPXCancelEvent(). Unlike most other ECB-related calls,
- IPXCancelEvent() does not return until the ECB has been cancelled so
- there is no need to poll the in use flag.
-
- Event Service Routines
-
- Event Service Routines (ESRs) serve the same purpose as the NetBIOS POST
- routines, but require a little more setup than the standard POST routine.
- Most ESRs are written in Assembly, although some call C functions.
-
- Figure 5 shows a generic ESR that calls a C function after allocating its
- own stack. This is very important since the amount of free stack space
- (if any) at interrupt time is unknown, and any attempt by a C function to
- use the stack could result in memory corruption if the stack is
- overflowed. The only way to guarantee that this will not occur is to
- allocate sufficient stack space in the ESR.
-
- *********************************************************
- Figure 5: Example Event Service Routine (ESR) [- C/ASM code -]
-
- .MODEL LARGE
-
- public _ReceiveESRHandler
- extrn _ProcessReceiveData:PROC
-
- .DATA
-
- ; The stack segment and pointer must be saved so that you can set up
- ; your own stack.
-
- stk_seg dw 0 ; variable to store old stack segment
- stk_ptr dw 0 ; variable to store old stack pointer
- stk_stk dw 512 dup (0) ; new stack of 1024 bytes in length
- stk_end dw 0 ; the end of the stack
-
- .CODE
-
- ; @datasize is TRUE if the model is MEDIUM or LARGE and FALSE if the
- ; model is SMALL or COMPACT. Just modify the .MODEL ???? above for the
- ; model you want. ES/SI holds the seg/offset of the currently used ECB
- ; that ProcessReceivedData needs to process.
-
- _ReceiveESRHandler PROC far
- mov ax,DGroup
- mov ds,ax
- mov stk_seg,ss ; Save the stack segment
- mov stk_ptr,sp ; Save the stack pointer
- mov ss,ax ; move the segment of new_stk into ss
- mov sp,offset stk_end ; move offset of new_stk to sp
- IF @datasize
- push es ; push es if mem. model medium/large
- ENDIF
- push si
- call _ProcessReceivedData
- mov ss,stk_seg ; Restore old stack segment
- mov sp,stk_ptr ; Restore old stack pointer
- retf
- _ReceiveESRHandler ENDP
-
- END END of FIGURE 5
- *********************************************************
-
-
- [- *********************************************************
- Figure 5a: Example Event Service Routine (ESR) (BASM/Pascal)
-
- { The stack segment and pointer must be saved so that you can set up
- your own stack. }
-
- Var stk_stk:array[1..512] of word; { new stack of 1024 bytes in length }
- stk_end:word; { the end of the stack }
-
- {$F+}
- Procedure ESRhandler(Var p:Tpecb); { * Type TPecb=^Tecb }
- begin
- .
- .
- end;
- {$F-}
-
- {$F+}
- Procedure ListenESR; assembler;
- asm { ES:SI are the only valid registers when entering this procedure ! }
- mov dx, seg stk_stk { = seg @DATA }
- mov ds, dx
-
- mov dx,ss { setup of a new local stack }
- mov bx,sp { ss:sp copied to dx:bx}
- mov ax,ds
- mov ss,ax
- mov sp,offset stk_end
- push dx { push old ss:sp on new stack }
- push bx
- push es { * push es:si on stack as local vars }
- push si { * }
- mov di,sp { * }
- push ss { * push address of local ptr on stack }
- push di { * }
-
- CALL EsrHandler
-
- add sp,4 { skip stack ptr-copy }
- pop bx { restore ss:sp from new stack }
- pop dx
- mov sp,bx
- mov ss,dx
- end;
- {$F-}
-
- Note that a local stack of 1024 bytes (512 words) may not be large
- enough for some applications calling other functions within the
- ESRhandler. Increase the stacksize by 1024 bytes at a time to
- determine the stack requirement.
-
-
- END END of FIGURE 5a
- ********************************************************* -]
-
- Figure 6 contains a code fragment demonstrating the use of an ESR. It
- receives ten SPX packets just like the example in Figure 3 does, but it
- uses an ESR instead of polling the in use flag. The assembly language
- routine from Figure 4 is declared as the ESR, and it in turn calls the
- C [-/Pascal-] function ProcessReceivedData().
-
- *********************************************************
- Figure 6: Using an Event Service Routine (ESR) [- C Code -]
-
- int packetCount = 0;
-
- void ProcessReceivedData(ECB *ecb)
- {
- packetCount++;
- printf("%s\n", ecb->fragmentDescriptor[1].address);
- SPXListenForSequencedPacket(ecb); /* Re-issue the listen */
- }
-
- main()
- {
- .
- . /* This code is identical to SPX setup code in Fig. 4, except */
- . /* for receive[i].ESRAddress line, which will be as follows: */
-
- receive[i].ESRAddress = (void (far *) () ) ReceiveESRHandler;
-
- .
- . /* The send ECB does not normally use an ESR. */
- .
-
- while (packetCount < 10)
- IPXRelinquishControl();
- .
- . /* Shut down connection, cancel ECBs */
- .
- }
-
- END of FIGURE 6
- *********************************************************
-
- [- *********************************************************
- Figure 6a: Using an Event Service Routine (ESR) (Pascal)
-
- Var PacketCount;
-
- Procedure ProcessReceivedData(Var ECB:Tecb)
- begin
- inc(packetCount);
- writeln(string(ecb^.fragment[2].address^));
- SPXListenForSequencedPacket(ecb); { Re-issue the listen }
- end;
-
- begin { main body }
- PacketCount:=0;
-
- .
- . { This code is identical to SPX setup code in Fig. 4a, except }
- . { for receive[i].ESRAddress line, which will be as follows: }
-
- receive[i].ESRAddress := @ReceiveESRHandler;
- .
- . { The send ECB does not normally use an ESR. }
- .
-
- while (packetCount < 10)
- do IPXRelinquishControl;
- .
- . { Shut down connection, cancel ECBs }
- .
- end;
-
- END of FIGURE 6a
- ********************************************************* -]
-
- IPX and SPX may look a little more complicated than NetBIOS at first, but
- as soon as you begin using these protocols, you see how similar they
- really are. Using IPX/SPX requires slightly more effort, but the
- performance and compatibility gains when running under NetWare more than
- compensate. If you are thinking about becoming more familiar with IPX and
- SPX development, feel free to contact Novell's Developer Support group at
- 1-800-NETWARE (1-800-638-9273) or 1-801-429-5588.
-